package resolver

import (
	"context"
	"go.etcd.io/etcd/api/v3/mvccpb"
	"go.etcd.io/etcd/client/v3"
	"log"
	"sync"
	"time"
)

type (
	Resolver struct {
		client3    *clientv3.Client  //etcd的链接
		serverList map[string]string //服务地址
		lock       sync.Mutex
	}
)

func NewClient(addr []string) (*Resolver, error) {
	conf := clientv3.Config{
		Endpoints:   addr,
		DialTimeout: 5 * time.Second,
	}

	if client, err := clientv3.New(conf); err == nil {
		return &Resolver{
			client3:    client,
			serverList: make(map[string]string),
		}, nil
	} else {
		return nil, err
	}
}

func (r *Resolver) GetService(prefix string) ([]string, error) {
	resp, err := r.client3.Get(context.Background(), prefix, clientv3.WithPrefix())
	if err != nil {
		return nil, err
	}
	adders := r.extractAdders(resp)

	go r.watcher(prefix)
	return adders, nil
}

func (r *Resolver) watcher(prefix string) {
	rch := r.client3.Watch(context.Background(), prefix, clientv3.WithPrefix())
	for wresp := range rch {
		for _, ev := range wresp.Events {
			switch ev.Type {
			case mvccpb.PUT:
				r.SetServiceList(string(ev.Kv.Key), string(ev.Kv.Value))
			case mvccpb.DELETE:
				r.DelServiceList(string(ev.Kv.Key))
			}
		}
	}
}

func (r *Resolver) extractAdders(resp *clientv3.GetResponse) []string {
	addrs := make([]string, 0)
	if resp == nil || resp.Kvs == nil {
		return addrs
	}
	for i := range resp.Kvs {
		if v := resp.Kvs[i].Value; v != nil {
			r.SetServiceList(string(resp.Kvs[i].Key), string(resp.Kvs[i].Value))
			addrs = append(addrs, string(v))
		}
	}
	return addrs
}

func (r *Resolver) SetServiceList(key, val string) {
	r.lock.Lock()
	defer r.lock.Unlock()
	r.serverList[key] = string(val)
	log.Println("set data key :", key, "val:", val)
}

func (r *Resolver) DelServiceList(key string) {
	r.lock.Lock()
	defer r.lock.Unlock()
	delete(r.serverList, key)
	log.Println("del data key:", key)
}

func (r *Resolver) SerList2Array() []string {
	r.lock.Lock()
	defer r.lock.Unlock()
	addrs := make([]string, 0)

	for _, v := range r.serverList {
		addrs = append(addrs, v)
	}
	return addrs
}
